//===-- SwiftSILManipulator.cpp ---------------------------------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

#include "SwiftSILManipulator.h"

#include "SwiftASTManipulator.h"

#include "lldb/Core/Log.h"
#include "lldb/Symbol/CompilerType.h"

#include "swift/SIL/SILArgument.h"
#include "swift/SIL/SILBasicBlock.h"
#include "swift/SIL/SILBuilder.h"
#include "swift/SIL/SILFunction.h"
#include "swift/SIL/SILLocation.h"
#include "swift/SIL/SILModule.h"
#include "swift/SIL/SILType.h"
#include "swift/SIL/SILValue.h"
#include "swift/SIL/TypeLowering.h"

using namespace lldb_private;

SwiftSILManipulator::SwiftSILManipulator(swift::SILBuilder &builder)
    : m_builder(builder),
      m_log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)) {}

swift::SILValue SwiftSILManipulator::emitLValueForVariable(
    swift::VarDecl *var, SwiftExpressionParser::SILVariableInfo &info) {
  swift::SILFunction &function = m_builder.getFunction();

  swift::SILBasicBlock &entry_block = *function.getBlocks().begin();

  swift::SILArgument *struct_argument = nullptr;

  for (swift::SILArgument *argument : entry_block.getArguments()) {
    swift::Identifier argument_name = argument->getDecl()->getName();

    if (!strcmp(argument_name.get(), SwiftASTManipulator::GetArgumentName())) {
      struct_argument = argument;
      break;
    }
  }

  if (!struct_argument)
    return swift::SILValue();

  assert(struct_argument->getType().getAsString().find(
             "UnsafeMutablePointer") != std::string::npos);

  swift::CanType unsafe_mutable_pointer_can_type =
      struct_argument->getType().getSwiftType();

  swift::BoundGenericStructType *unsafe_mutable_pointer_struct_type =
      llvm::cast<swift::BoundGenericStructType>(
          unsafe_mutable_pointer_can_type.getPointer());
  swift::StructDecl *unsafe_mutable_pointer_struct_decl =
      unsafe_mutable_pointer_struct_type->getDecl();

  swift::VarDecl *value_member_decl = nullptr;

  for (swift::Decl *member : unsafe_mutable_pointer_struct_decl->getMembers()) {
    if (swift::VarDecl *member_var = llvm::dyn_cast<swift::VarDecl>(member)) {
      if (member_var->getName().str().equals("_rawValue")) {
        value_member_decl = member_var;
        break;
      }
    }
  }

  if (!value_member_decl)
    return swift::SILValue();

  swift::ASTContext &ast_ctx = m_builder.getASTContext();
  swift::Lowering::TypeConverter converter(m_builder.getModule());

  swift::SILLocation null_loc((swift::Decl *)nullptr);
  swift::SILType raw_pointer_type = swift::SILType::getRawPointerType(ast_ctx);

  swift::StructExtractInst *struct_extract = m_builder.createStructExtract(
      null_loc, struct_argument, value_member_decl, raw_pointer_type);
  swift::IntegerLiteralInst *integer_literal = m_builder.createIntegerLiteral(
      null_loc, swift::SILType::getBuiltinIntegerType(64, ast_ctx),
      (intmax_t)info.offset);
  swift::IndexRawPointerInst *index_raw_pointer =
      m_builder.createIndexRawPointer(null_loc, struct_extract,
                                      integer_literal);
  swift::PointerToAddressInst *pointer_to_return_slot =
      m_builder.createPointerToAddress(null_loc, index_raw_pointer,
                                       raw_pointer_type.getAddressType(),
                                       /*isStrict*/ true);
  swift::LoadInst *pointer_to_variable =
      m_builder.createLoad(null_loc, pointer_to_return_slot,
                           swift::LoadOwnershipQualifier::Trivial);
  auto type = var->getDeclContext()->mapTypeIntoContext(
      var->getInterfaceType());
  swift::PointerToAddressInst *address_of_variable =
      m_builder.createPointerToAddress(
          null_loc, pointer_to_variable,
          converter.getLoweredType(type).getAddressType(),
          /*isStrict*/ true);

  if (info.needs_init) {
    info.needs_init = false;
    return swift::SILValue(m_builder.createMarkUninitialized(
        null_loc, address_of_variable, swift::MarkUninitializedInst::Var));
  } else
    return swift::SILValue(address_of_variable);
}
